﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Reflection;

namespace Tecnicas.CasoEstudio.Repositorio
{
    public class RepositorioNoVersionado
    {
        private string repositoryPhysicalPath = Path.GetDirectoryName(Assembly.GetAssembly(typeof(RepositorioVersionado)).CodeBase).Replace("file:\\", "") + "\\RepositorioNoVersionado.items.dat";
        private RepositorioNoVersionado() 
        {
            // Incializa el repositorio a partir de su versión persistida
            this.Cargar();
        }

        private static RepositorioNoVersionado _instance = null;
        /// <summary>
        /// Obtiene la instancia única del repositorio
        /// </summary>
        public static RepositorioNoVersionado Instance
        {
            get
            {
                if (RepositorioNoVersionado._instance == null) RepositorioNoVersionado._instance = new RepositorioNoVersionado();
                return RepositorioNoVersionado._instance;
            }
        }

        private Hashtable items = new Hashtable();        


        /// <summary>
        /// Persiste el repositorio
        /// </summary>
        public void Persistir()
        {
            
            Serializer.SerializeObject(repositoryPhysicalPath, items);
        }
        private void Inicializar()
        {
            items = new Hashtable();
        }
        private void Cargar()
        {
            if (File.Exists(repositoryPhysicalPath))
                items = (Hashtable)Serializer.DeSerializeObject(repositoryPhysicalPath);
        }


        public object ObtenerPorId(Type tipo, long? id)
        {
            if (!this.Existe(tipo, id))
                throw new ApplicationException("No existe el objeto con dicho id");

            return ((Hashtable)items[tipo])[id];          
        }

        public List<Object> ObtenerTodos(Type tipo)
        {
            if (!this.ExistenDeEsteTipo(tipo))
                return new List<object>();

            return (List<Object>)((Hashtable)items[tipo]).Values;

        }

        private bool ExistenDeEsteTipo(Type tipo) 
        {
            return !(items[tipo] == null);
        }

        private bool Existe(Type tipo, long? id)
        {
            if (!this.ExistenDeEsteTipo(tipo))
                return false;

            return !(((Hashtable)items[tipo])[id] == null);
        }

        public void Guardar(object obj, long? id)
        {
            if (!this.ExistenDeEsteTipo(obj.GetType())) 
            {
                items[obj.GetType()] = new Hashtable();
                ((Hashtable)items[obj.GetType()])[id] = this.Clonar(obj);
                return;
            }

            if (!this.Existe(obj.GetType(), id)) 
            {
                ((Hashtable)items[obj.GetType()])[id] = this.Clonar(obj);
                return;
            }

            ((Hashtable)items[obj.GetType()])[id] = obj;
            // recursivamente guardar el obj en los hash de sus clases bases e interfaces
        }

        public void Eliminar(Type type, long? id)
        {
            if (!this.ExistenDeEsteTipo(type))
                throw new ApplicationException("No existe el objeto a eliminar");
            
            if (!this.Existe(type, id))
                throw new ApplicationException("No existe el objeto a eliminar");
            
            ((Hashtable)items[type])[id] = null;
        }

        private object Clonar(object objectToClone)
        {
            MemoryStream ms = new MemoryStream();
            BinaryFormatter bf = new BinaryFormatter();
            bf.Serialize(ms, objectToClone);
            ms.Position = 0;
            object obj = bf.Deserialize(ms);
            ms.Close();
            return obj;
        }
    }
}
